package wangwenjun.phase2.concurrencypattern.threadcontext.context;

import lombok.Data;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ChangLiang
 * @date 2020/6/15
 */
@Data
public class ApplicationContextV2 {

    /**
     * ApplicationContext是系统上下文，其生命周期会随着被创建一直到系统运行结束
     * 其系统上下文成员configuration以及runtimeInfo 与其生命周期相同
     *
     * 考虑点1：ApplicationContext需要系统内全局唯一
     * 解决方案：通过单例模式来保证
     *
     * 考虑点2：系统上下文成员只能被初始化一次 比如ApplicationConfiguration
     *
     * 考虑点3：在多线程环境下，系统上下文成员的线程安全性
     * 解决方案：如Single Thread Execution模式(synchronized) 或者 不可变对象设计模式 或者CAS(AtomicInteger) 等资源保护方法
     */

    private ApplicationConfiguration configuration;

    private RuntimeInfo runtimeInfo;

    /**
     * 我们这里的实现是使用了ConcurrentHashMap集中存储
     * 而JDK中的ThreadLocal 是将这个Map放到Thread类中 其key为ThreadLocal对象，value就是ThreadContext
     *
     * 通过这种方式定义线程上下文很可能会导致内存泄漏，contexts是一个Map的数据结构，
     * 用当前线程做key，当线程的生命周期结束后，contexts中的Threads实例不会被释放，与之对应的value也不会被释放，时间长了就会导致内存泄漏。
     *
     * 当然可以通过soft reference或者weak referenced等引用类型，JVM会主动尝试回收。
     *
     *
     */
    private ConcurrentHashMap<Thread, ThreadContext> contexts = new ConcurrentHashMap<>();

    public ThreadContext getThreadContext() {
        ThreadContext threadContext = contexts.get(Thread.currentThread());
        if (threadContext == null) {
            threadContext = new ThreadContext();
            contexts.put(Thread.currentThread(), threadContext);
        }
        return threadContext;
    }

    private static class Holder{
        private static ApplicationContextV2 instance = new ApplicationContextV2();
    }

    public static ApplicationContextV2 getApplicationContext() {
        return Holder.instance;
    }
}
